package com.zzyl.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONUtil;
import com.aliyun.iot20180120.Client;
import com.aliyun.iot20180120.models.*;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.zzyl.base.PageResponse;
import com.zzyl.constant.Constants;
import com.zzyl.dto.DeviceDataPageReqDto;
import com.zzyl.dto.DeviceDto;
import com.zzyl.dto.DevicePageQueryDto;
import com.zzyl.entity.Content;
import com.zzyl.entity.Device;
import com.zzyl.entity.DeviceData;
import com.zzyl.enums.BasicEnum;
import com.zzyl.exception.BaseException;
import com.zzyl.mapper.DeviceDataMapper;
import com.zzyl.mapper.DeviceMapper;
import com.zzyl.properties.AliIoTConfigProperties;
import com.zzyl.service.IotService;
import com.zzyl.vo.DeviceDataGraphVo;
import com.zzyl.vo.DeviceDataVo;
import com.zzyl.vo.DeviceVo;
import com.zzyl.vo.ProductVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

@Slf4j
@Service
public class IotServiceImpl implements IotService {

    @Autowired
    private Client client;
    @Autowired
    private AliIoTConfigProperties iotProp;
    @Autowired
    private StringRedisTemplate redisTemplate;
    @Autowired
    private DeviceMapper deviceMapper;
    @Override
    public void syncProductList() {
        int page = 1;
        while (true){
//            调用sdk方法，请求iot平台，获取数据
            QueryProductListRequest request =  new QueryProductListRequest();
            request.setCurrentPage(page);
            request.setPageSize(1);
            request.setIotInstanceId(iotProp.getIotInstanceId());
            QueryProductListResponse response = null;
            try {
                response = client.queryProductList(request);
            } catch (Exception e) {
                e.printStackTrace();
            }
            if (response == null){
                log.error("获取数据失败");
                throw new BaseException(BasicEnum.IOT_LIST_PRODUCT_ERROR);
            }
            Boolean b = response.getBody().getSuccess();
            if (!b){
                log.error("获取数据失败");
                throw new BaseException(BasicEnum.IOT_LIST_PRODUCT_ERROR);
            }
            QueryProductListResponseBody.QueryProductListResponseBodyData data = response.getBody().getData();
            QueryProductListResponseBody.QueryProductListResponseBodyDataList list = data.getList();
            List<QueryProductListResponseBody.QueryProductListResponseBodyDataListProductInfo> productInfoList = list.getProductInfo();
            for (QueryProductListResponseBody.QueryProductListResponseBodyDataListProductInfo info : productInfoList) {
//                存入Redis中，1个key可以对应一个产品的集合  还是一个key对应一个产品
                redisTemplate.opsForHash().put("iot:product",info.getProductKey(), JSONUtil.toJsonStr(info));
            }
            if (data.getPageCount() > data.getCurrentPage()){
                page ++;
            } else {
                break;
            }
        }
    }

    @Override
    public List<ProductVo> allProduct() {
//        没有访问redis，只是创建了一个对象
        BoundHashOperations<String, String, String> hashOps = redisTemplate.boundHashOps("iot:product");
        List<String> values = hashOps.values();

//        for循环
//        List<ProductVo> voList = new ArrayList<>();
//        for (String value : values) {
//            ProductVo productVo = JSONUtil.toBean(value, ProductVo.class);
//            voList.add(productVo);
//        }

//        流
        List<ProductVo> voList = values
                .stream()
                .map(str -> {
                    return JSONUtil.toBean(str, ProductVo.class);
                })
                .collect(Collectors.toList());
        return voList;
    }

    @Override
    public void registerDevice(DeviceDto dto) {
//        查询设备名称是否存在
        int count = deviceMapper.selectByDeviceName(dto.getDeviceName());
        if (count > 0){
            log.error("设备名称已存在");
            throw new BaseException(BasicEnum.DEVICE_NAME_EXIST);
        }
//        调用iot平台,创建设备
        RegisterDeviceRequest request = new RegisterDeviceRequest();
        request.setIotInstanceId(iotProp.getIotInstanceId());
        request.setDeviceName(dto.getDeviceName());
        request.setNickname(dto.getNickname());
        request.setProductKey(dto.getProductKey());
        RegisterDeviceResponse response = null;
        try {
            response = client.registerDevice(request);
        } catch (Exception e) {
            e.printStackTrace();
        }
        if (response == null || !response.getBody().getSuccess()){
            log.error("调用iot注册设备失败");
            throw new BaseException(BasicEnum.IOT_REGISTER_DEVICE_ERROR);
        }
//        获取设备的id
        String iotId = response.getBody().getData().getIotId();
//        根据产品key,查询产品信息
        BoundHashOperations<String, String, String> hashOps = redisTemplate.boundHashOps("iot:product");
        String json = hashOps.get(dto.getProductKey());
//        if (StringUtils.isBlank(json)){
//            调用IOT平台查询 产品列表,如果查询回来的数据依旧是空的,就报错
//        }
        ProductVo productVo = JSONUtil.toBean(json, ProductVo.class);
//        把产品名称设置到设备对象中
        Device device = BeanUtil.toBean(dto, Device.class);
        device.setProductName(productVo.getProductName());
        device.setIotId(iotId);
//        不包含门禁
        device.setHaveEntranceGuard(0);
//        如果位置类型是0-随身设备,把设备的物理位置设置为-1(人)
        if (dto.getLocationType() == 0){
            device.setPhysicalLocationType(-1);
        }
        try {
//        保存设备表数据
            int num = deviceMapper.insert(device);
            if (num != 1){
                log.error("新增设备失败");
                throw new BaseException(BasicEnum.DEVICE_SAVE_ERROR);
            }
        } catch (Exception e){
            e.printStackTrace();
//        如果保存失败,调用iot接口,删除iot的设备数据
            DeleteDeviceRequest request1 = new DeleteDeviceRequest();
            request1.setIotId(iotId);
            request1.setIotInstanceId(iotProp.getIotInstanceId());
            try {
                DeleteDeviceResponse response1 = client.deleteDevice(request1);
            } catch (Exception exception) {
                exception.printStackTrace();
                log.error("调用IOT删除设备失败");
            }
        }
    }

    @Override
    public PageResponse<DeviceVo> findByPage(DevicePageQueryDto dto) {

        PageHelper.startPage(dto.getPageNum(), dto.getPageSize());

        List<DeviceVo> voList = deviceMapper.seleceByCondition(dto);
        Page page = (Page) voList;

        return PageResponse.of(page, DeviceVo.class);
    }

    @Override
    public DeviceVo findDeviceDetail(DeviceDto dto) {
//        根据产品和设备id 查询iot平台
        QueryDeviceDetailRequest request = new QueryDeviceDetailRequest();
        request.setIotInstanceId(iotProp.getIotInstanceId());
        request.setIotId(dto.getIotId());
        QueryDeviceDetailResponse response = null;
        try {
            response = client.queryDeviceDetail(request);
        } catch (Exception e) {
            e.printStackTrace();
        }
        if (response == null || !response.getBody().getSuccess()){
            log.error("调用iot查询设备详情失败");
            throw new BaseException(BasicEnum.IOT_QUERY_DEVICE_ERROR);
        }
        QueryDeviceDetailResponseBody.QueryDeviceDetailResponseBodyData data = response.getBody().getData();
//        属性拷贝
        DeviceVo deviceVo = BeanUtil.toBean(data, DeviceVo.class);
//        根据设备id查询设备
        Device device = deviceMapper.selectByIotId(dto.getIotId());
//        合并数据
        BeanUtils.copyProperties(device, deviceVo);
        return deviceVo;
    }

    @Autowired
    private DeviceDataMapper deviceDataMapper;

    @Override
    public void batchSaveDeviceData(Content contentObj) {
//        根据iotid 查询设备
        Device device = deviceMapper.selectByIotId(contentObj.getIotId());
//        如果设备不存在，任务结束
        if (device == null){
            log.error("{}=设备不存在，无法保存",contentObj.getIotId());
        }
//        构造保存的数据结构，执行保存操作
        List<DeviceData> list = new ArrayList<>();
        Map<String, Content.Item> items = contentObj.getItems();
        items.forEach((key,val) -> {
            DeviceData deviceData = DeviceData.builder()
                    .deviceName(device.getDeviceName())
                    .iotId(contentObj.getIotId())
                    .nickname(device.getNickname())
                    .productKey(device.getProductKey())
                    .productName(device.getProductName())
                    .functionId(key)
                    .accessLocation(device.getBindingLocation())
                    .locationType(device.getLocationType())
                    .physicalLocationType(device.getPhysicalLocationType())
                    .deviceDescription(device.getDeviceDescription())
                    .dataValue(val.getValue()+"")
                    .alarmTime(LocalDateTimeUtil.of(val.getTime()))
                    .build();
            list.add(deviceData);
        });
        deviceDataMapper.batchInsert(list);
//        把最后一条数据保存到Redis
        redisTemplate.opsForHash().put(Constants.DEVICE_LASTDATA_CACHE_KEY,contentObj.getIotId(),JSONUtil.toJsonStr(list));
    }

    @Override
    public PageResponse<DeviceDataVo> getDeviceDataPage(DeviceDataPageReqDto dto) {
        PageHelper.startPage(dto.getPageNum(), dto.getPageSize());
        //前端传递的时间是一个long类型的毫秒值，作为条件查询数据库需要转换为日期类型
        Long startTime = dto.getStartTime();
        Long endTime = dto.getEndTime();
        dto.setStartDate(ObjectUtil.isEmpty(startTime) ? null : LocalDateTimeUtil.of(startTime));
        dto.setEndDate(ObjectUtil.isEmpty(endTime) ? null : LocalDateTimeUtil.of(endTime));
        //查询
        Page<DeviceDataVo> page = deviceDataMapper.selectByPage(dto);

        return PageResponse.of(page, DeviceDataVo.class);
    }

    @Override
    public QueryDevicePropertyStatusResponseBody.QueryDevicePropertyStatusResponseBodyData queryDevicePropertyStatus(DeviceDto dto) {
//        调用iot的api
        QueryDevicePropertyStatusRequest request = new QueryDevicePropertyStatusRequest();
        request.setIotInstanceId(iotProp.getIotInstanceId());
        request.setProductKey(dto.getProductKey());
        request.setDeviceName(dto.getDeviceName());
        QueryDevicePropertyStatusResponse response = null;
        try {
            response = client.queryDevicePropertyStatus(request);
        } catch (Exception e) {
            e.printStackTrace();
        }
        if (response == null || !response.getBody().getSuccess()){
            log.error("调用iot查询设备属性状态失败");
            throw new BaseException(BasicEnum.IOT_QUERY_DEVICE_ERROR);
        }
        QueryDevicePropertyStatusResponseBody.QueryDevicePropertyStatusResponseBodyData data = response.getBody().getData();
        return data;
    }

    @Override
    public List<DeviceDataGraphVo> queryDeviceDataListByDay(String iotId, String functionId, Long startTime, Long endTime) {

//        把时间戳转LocalDateTime类型  begin 00:00 end 23:00
        LocalDateTime begin = LocalDateTimeUtil.of(startTime);
        LocalDateTime end = LocalDateTimeUtil.of(endTime);
        List<DeviceDataGraphVo> voList = deviceDataMapper.queryDeviceDataListByDay(iotId,functionId,begin,end);
        List<DeviceDataGraphVo> result = new ArrayList<>();
//        第一种写法
//        for (int i = 0; i < 24; i++){
//            boolean b = false;
//            DeviceDataGraphVo vo = null;
//            for (DeviceDataGraphVo deviceDataGraphVo : voList) {
//                if (begin.equals(deviceDataGraphVo.getDateTime())){
////                    如果时间在数据库中存在，就设置为true，并结束循环
//                    vo= deviceDataGraphVo;
//                    b = true;
//                    break;
//                }
//            }
//            if (!b){
////                如果时间不在数据库中存在 就自己创建
//                vo = new DeviceDataGraphVo();
//                vo.setDateTime(begin.format(DateTimeFormatter.ofPattern("HH:00")));
//                vo.setDataValue(0.0);
//            }
//            result.add(vo);
////            给begin加1小时
//            begin = begin.plusHours(1);
//        }
//        第二种写法
//        把数据库返回的集合进行处理，处理成Map结构 Map<dataTime,DeviceDataGraphVo> 使用流处理   Function.identity() 代表当前流遍历出来的对象
        Map<String, DeviceDataGraphVo> map = voList.stream().collect(Collectors.toMap(DeviceDataGraphVo::getDateTime, Function.identity()));
        for (int i = 0; i < 24; i++){
            String beginStr = begin.format(DateTimeFormatter.ofPattern("HH:00"));
            DeviceDataGraphVo vo = map.getOrDefault(beginStr, new DeviceDataGraphVo(beginStr, 0.0));
            result.add(vo);
            begin = begin.plusHours(1);
        }
        return result;
    }

    @Override
    public List<DeviceDataGraphVo> queryDeviceDataListByWeek(String iotId, String functionId, Long startTime, Long endTime) {
        List<DeviceDataGraphVo> result = new ArrayList<>();
//        把时间戳转LocalDateTime类型  begin 2024-06-07 00:00 end 2024-06-13 23:00
        LocalDateTime begin = LocalDateTimeUtil.of(startTime);
        LocalDateTime end = LocalDateTimeUtil.of(endTime);
        List<DeviceDataGraphVo> voList = deviceDataMapper.selectDeviceDataListByWeek(iotId,functionId,begin,end);

        Map<String, DeviceDataGraphVo> map = voList.stream().collect(Collectors.toMap(DeviceDataGraphVo::getDateTime, Function.identity()));
        for (int i = 0; i < 7; i++){
            String beginStr = begin.format(DateTimeFormatter.ofPattern("MM.dd"));
            DeviceDataGraphVo vo = map.getOrDefault(beginStr, new DeviceDataGraphVo(beginStr, 0.0));
            result.add(vo);
            begin = begin.plusDays(1);
        }
        return result;
    }

    @Override
    public QueryThingModelPublishedResponseBody.QueryThingModelPublishedResponseBodyData queryThingModelPublished(DeviceDto dto) {
//        调用Aliyun 的sdk
        QueryThingModelPublishedRequest request = new QueryThingModelPublishedRequest();
        request.setProductKey(dto.getProductKey());
        request.setIotInstanceId(iotProp.getIotInstanceId());
        QueryThingModelPublishedResponse response = null;
        try {
            response = client.queryThingModelPublished(request);
        } catch (Exception e) {
            e.printStackTrace();
        }
        if (response == null || !response.getBody().getSuccess()){
            log.error("调用iot查询物模型发布失败");
            throw new BaseException(BasicEnum.IOT_QUERY_THING_MODEL_PUBLISHED_ERROR);
        }
        return response.getBody().getData();
    }
}
